#!/usr/bin/env python
# Raja S

# Time-stamp: <Last  modified by Raja S on 18.03.2009 at 21:34:17 on tp>

'''
cmdtutor provides the mechanism to 'execute' command line tutorials written
in a standard format. It acts as an interface between the user and the 
terminal.

Author: Raja (rajajs@gmail.com)

version 0.4, Mar 2009
             - Refactored to better OO design
             - workingfolder is copied into a tmp directory for lessons
             - tab completion works
             - can choose another lesson on completing one
version 0.3, 24 Mar 2007 - added quickflag
             04 Apr 2007 - added wrapping            
Version: 0.2, 6 Mar 2007
'''

from optparse import OptionParser
import os
import sys
import time
import readline
import textwrap
import shutil
import commands
import tempfile

readline.parse_and_bind("tab: complete")

class Tutor():
    """The command line tutor"""
    def __init__(self, optparser):
        """Initialize the tutor.
        Parse the arguments and get color codes"""
        self.getoptions(optparser)
        self.getcolorcodes()
        self.pause = 0.2 # TODO :
        self.prompt = 'Tutor>>> '

        self.systemlessonpath = '/usr/share/cmd-tutor/lessons'
        self.workingfolder = '/usr/share/cmd-tutor/testfolder'
        self.locallessonpath = os.path.join(os.curdir, '../share/lessons')

        self.homedir = os.getenv("HOME")#commands.getoutput('echo $HOME')
        self.termwidth = int(commands.getoutput('echo $COLUMNS'))
    
    def getcolorcodes(self):
        """Obtain the codes for formatting in the terminal"""
        self.colorcode = {}
        self.colorcode['BOLD']   = os.popen("tput bold").read()
        self.colorcode['NOBOLD'] = os.popen("tput sgr0").read()
        self.colorcode['RED']    = os.popen("tput setaf 1").read()
        self.colorcode['NORMAL'] = os.popen("tput setaf 9").read()

    def getoptions(self, optparser):
        """parse the supplied arguments"""
        optparser.add_option("-q", "--quick", action="store_true", dest="quickprint",
                             default=False, help="Dont pause while printing")
        optparser.add_option("-l", "--lessons", dest="lessonpath",
                             default=False, help="directory containing lessons")
        self.options, args = optparser.parse_args()
        self.quickprint = self.options.quickprint
        self.defaultlessonpath = self.options.lessonpath

    def pauseprint(self, text):
        """Print the words in text one by one with 
        'pause' seconds between them"""
        if self.quickprint:
            print text

        else:        
            for word in text.split():
                print word,
                sys.stdout.flush()
                time.sleep(self.pause)
            print '\n'

    def blockParse(self, block):
        chunks = block.split(':')
        instruction = chunks[1].rstrip('Error')
        error = chunks[2].rstrip('Next')
        next = chunks[3]
        return (instruction,error,next)

    def getlessonpath(self):
        """interactively obtain path to directory containing lessons.
        If the system default lessons directory or local lessons
        directory exists, present it as default"""
        defaultlessonpath = None
        if os.path.exists(self.systemlessonpath):
           defaultlessonpath = self.systemlessonpath
        else:
            if os.path.exists(self.locallessonpath):
                defaultlessonpath = os.path.abspath(self.locallessonpath)

        if defaultlessonpath:
            self.lessonpath = raw_input\
                ("Please enter the path to lessons, [%s] >>" %(defaultlessonpath))
            if self.lessonpath == '':
                self.lessonpath = defaultlessonpath
        else:
            self.lessonpath = raw_input\
                ("Please enter the location of the lessons eg. /home/lessons >>")
        
        while not os.path.exists(self.lessonpath):
            self.lessonpath = raw_input\
                ("Please enter the location of the lessons eg. /home/lessons >>")

        self.lessons = os.listdir(self.lessonpath)
        self.lessons.sort()

    def chooselesson(self):
        """Choose lesson to use from list of lessons"""
        if len(self.lessons) == 0:
            print "There are no lessons in the directory"
            sys.exit() #TODO: ask to choose lesson again
        elif len(self.lessons) == 1:
            print '%s found. Starting lesson' %(self.lesson[0])
        else:
            print 'Found following lessons. Choose lesson to start'
            for ind, lesson in enumerate(self.lessons):
                print '%s\t%s' %(ind+1, lesson)

        chosenlesson = 0
        while chosenlesson != 'q' and \
                       int(chosenlesson) not in range(1,len(self.lessons)+1):
            chosenlesson = raw_input("Enter number of lesson [1-%s] or 'q' to quit - "
                                                  %(len(self.lessons)))
        if chosenlesson == 'q':
            sys.exit()
        else:
            return os.path.abspath(os.path.join(
                    self.lessonpath, self.lessons[int(chosenlesson)-1])) 

    def walkthrolesson(self, lessonpath):
        """open, parse and walk through lesson"""
        lesson = open(lessonpath, 'r')
        parsedlesson = self.parselesson(lesson)

        for block in parsedlesson:
            blockcompleted = False
            command = 'dummycommand'
            instr = block['instr'].replace('[',self.colorcode['RED']).replace(']',
                                               self.colorcode['NORMAL']) 
            nxt = block['nxt'].replace('command', 'command.split(' ')')\
                              .replace('output', 'commands.getoutput')
            err = block['err'].replace('[',self.colorcode['RED']).replace(']',
                                             self.colorcode['NORMAL']) 

            print '\n' +  '-'*self.termwidth  
            for line in textwrap.wrap(instr, self.termwidth):
                self.pauseprint(line)
            
            while not eval(nxt) or not blockcompleted:
                print self.colorcode['BOLD']
                command = raw_input(self.prompt)
                print self.colorcode['NOBOLD']+ ''
                
                if command == 'quit':
                    self.quitornewlesson()
                elif command.startswith('cd '):
                    targetdir = command.lstrip('cd ').replace('~', self.homedir)
                    if os.path.isdir(targetdir):
                        os.chdir(targetdir)
                        blockcompleted = True # TODO
                else:
                    status = os.system(command) # TODO: get error from system
                    if status != 0:
                        self.pauseprint(err)
                        self.pauseprint('Check if you typed the command correctly') 
                    else:
                        blockcompleted = True
                        
        return None

    def quitornewlesson(self):
        """activated by typing quit.
        Choose to quit or see main menu again"""
        choice = raw_input("Press enter to quit or any key to select another lesson: ")
        if choice == '':
            sys.exit()
        else:
            chosenlesson = self.chooselesson()
            self.walkthrolesson(chosenlesson)

    def parselesson(self, lesson):
        """parse the file contents.
        The structure returned is a list, 
        each element is a dict representing a block in the lesson"""
        parsedlesson = []
        lessontext = lesson.read()
        blocks = lessontext.split('%%')
        
        for block in blocks:
            blockdict = {}
            blockdict['instr'], blockdict['err'], blockdict['nxt'] = \
                                self.blockParse(block)
            parsedlesson.append(blockdict)
        
        return parsedlesson

    def cd_workingfolder(self):
        """Make temporary folder and copy working folder into it"""
        self.rootfolder = tempfile.mkdtemp()
        self.workingfolder_copy = os.path.join(self.lessonpath, '../workingfolder')
        self.workingfolder = os.path.join(self.rootfolder, 'workingfolder')
        shutil.copytree(self.workingfolder_copy, self.workingfolder)
        os.chdir(self.workingfolder)

    def mainloop(self):
        """Parse lesson, print and obtain input"""
        self.getlessonpath()
        self.cd_workingfolder()
        chosenlesson = self.chooselesson()
        self.walkthrolesson(chosenlesson)
    
def main():
    optparser = OptionParser()
    tutor = Tutor(optparser)
    tutor.mainloop()


if __name__ == "__main__":
    main()

